package netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import util.ByteBufferUtil;

import java.net.InetSocketAddress;
import java.util.Random;

@Slf4j
public class HelloClient {
  public static void main(String[] args) {
    sendLengthField();
  }

  public static void hello() throws InterruptedException {
    Channel channel = new Bootstrap()
            .group(new NioEventLoopGroup())
            .channel(NioSocketChannel.class)
            .handler(new ChannelInitializer<NioSocketChannel>() {
              @Override
              protected void initChannel(NioSocketChannel channel) throws Exception {
                channel.pipeline().addLast(new StringEncoder());
              }
            })
            .connect(new InetSocketAddress("localhost", 8099))
            .sync()
            .channel();
    channel.writeAndFlush("hello netty");
  }

  public static void start() {
    NioEventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap bt = new Bootstrap();
//      bt.option(ChannelOption.SO_SNDBUF,10);
      bt.channel(NioSocketChannel.class)
              .group(group)
              .handler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                      ByteBuf buf = ctx.alloc().buffer(16);
                      byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 14, 15,16,17};
                      buf.writeBytes(bytes);
                      ctx.writeAndFlush(buf);
                      ctx.channel().close();
                    }
                  });
                }
              });
      ChannelFuture future = bt.connect("localhost", 8080).sync();
      future.channel().closeFuture().sync();
    } catch (Exception e) {
      log.error("server error");
      e.printStackTrace();
    } finally {
      group.shutdownGracefully();
    }
  }

  public static void sendFixedLength(){
    NioEventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap bt = new Bootstrap();
      bt.channel(NioSocketChannel.class)
              .group(group)
              .handler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                      ByteBuf buf = ctx.alloc().buffer();
                      char c = '0';
                      Random r = new Random();
                      for (int i = 0; i < 10; i++) {
                        byte[] bytes = ByteBufferUtil.fill10bytes(c, r.nextInt(10) + 1);
                        c++;
                        buf.writeBytes(bytes);
                      }
                      ctx.writeAndFlush(buf);
                    }
                  });
                }
              });
      ChannelFuture future = bt.connect("localhost", 8080).sync();
      future.channel().closeFuture().sync();
    } catch (Exception e) {
      log.error("server error");
      e.printStackTrace();
    } finally {
      group.shutdownGracefully();
    }
  }

  public static void sendLine(){
    NioEventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap bt = new Bootstrap();
      bt.channel(NioSocketChannel.class)
              .group(group)
              .handler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                      ByteBuf buf = ctx.alloc().buffer();
                      char c = '0';
                      Random r = new Random();
                      for (int i = 0; i < 10; i++) {
                        StringBuilder sb = ByteBufferUtil.makeString(c, r.nextInt(256)+1);
                        c++;
                        buf.writeBytes(sb.toString().getBytes());
                      }
                      ctx.writeAndFlush(buf);
                    }
                  });
                }
              });
      ChannelFuture future = bt.connect("localhost", 8080).sync();
      future.channel().closeFuture().sync();
    } catch (Exception e) {
      log.error("server error");
      e.printStackTrace();
    } finally {
      group.shutdownGracefully();
    }
  }

  public static void sendLengthField(){
    NioEventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap bt = new Bootstrap();
      bt.channel(NioSocketChannel.class)
              .group(group)
              .handler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                  ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                      ByteBuf buf = ctx.alloc().buffer();
                      byte[] bytes = "Hello, Netty".getBytes();
                      buf.writeInt(bytes.length);
                      buf.writeByte(1);
                      buf.writeBytes(bytes);
                      ctx.writeAndFlush(buf);
                    }
                  });
                }
              });
      ChannelFuture future = bt.connect("localhost", 8080).sync();
      future.channel().closeFuture().sync();
    } catch (Exception e) {
      log.error("server error");
      e.printStackTrace();
    } finally {
      group.shutdownGracefully();
    }
  }

  public static void start1(){
    NioEventLoopGroup worker = new NioEventLoopGroup();
    try {
      Bootstrap bootstrap = new Bootstrap();
      bootstrap.option(ChannelOption.SO_SNDBUF,10);
      bootstrap.channel(NioSocketChannel.class);
      bootstrap.group(worker);
      bootstrap.handler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
          log.debug("connected...");
          ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
              log.debug("sending...");
              // 每次发送16个字节的数据，共发送10次
              for (int i = 0; i < 10; i++) {
                ByteBuf buffer = ctx.alloc().buffer();
                buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
                ctx.writeAndFlush(buffer);
              }
            }
          });
        }
      });
      ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
      channelFuture.channel().closeFuture().sync();

    } catch (InterruptedException e) {
      log.error("client error", e);
    } finally {
      worker.shutdownGracefully();
    }
  }
}
